const PHONE = !!window.navigator.userAgent.match(/phone|iphone|android|ipad|ios|ipad/gi);
const [touchstart, touchmove, touchend, touchleave] = PHONE
    ? ['touchstart', 'touchmove', 'touchend']
    : ['mousedown', 'mousemove', 'mouseup', 'mouseleave'];

export class PullRefresh {
    constructor(options = {}) {
        this.$ele = options.$ele || document.body;
        this.options = options;
        this.init();
    }

    _touchstart = event => {
        const state = this.state;
        this.reset();
        const scrollTop = document.body.scrollTop || $(window).scrollTop();
        if (!state.disable && scrollTop == 0) {
            state.scrollTop = scrollTop;
            state.isStart = true;
            state.start = {
                x: event.touches ? event.touches[0].clientX : event.clientX,
                y: event.touches ? event.touches[0].clientY : event.clientY,
            };
            // 缓存高度
            state.innerHeight = window.innerHeight;
        }
    };

    _touchmove = event => {
        const { state } = this;
        if (state.isStart && !state.disable) {
            state.move = {
                x: event.touches ? event.touches[0].clientX : event.clientX,
                y: event.touches ? event.touches[0].clientY : event.clientY,
            };
            const { move, start } = state;
            state.H = ((move.y - start.y) / state.innerHeight) * 180;

            if (state.preventDefault || (!state.swipeDir && state.scrollTop == 0 && state.H > 0)) {
                // 获取滑动方向
                if (!state.swipeDir) {
                    state.swipeDir = Math.abs(move.x - start.x) <= Math.abs(move.y - start.y) ? 'ud' : 'lr';
                }

                if (state.swipeDir === 'ud') {
                    state.preventDefault = true;
                    event.preventDefault();
                    // 触发start回调
                    if (state.canStart) {
                        this.options.onStart && this.options.onStart();
                        state.canStart = false;
                    }
                    // 触发change回调
                    const { H } = state;
                    requestAnimationFrame(() => {
                        this.options.onChange && this.options.onChange(H);
                    });
                }
            }

            if (!state.preventDefault && !state.swipeDir && state.H < 0) {
                state.swipeDir = 'up';
            }
        }
    };

    _touchend = event => {
        const { state } = this;
        const { H, preventDefault, isStart } = state;
        this.reset();
        if (isStart && !state.disable && preventDefault) {
            event.preventDefault();
            // 因为touchmove使用了 requestAnimationFrame 因此在touchend的时候也要使用
            // 不然有可能导致touchend之后touchmove又执行
            requestAnimationFrame(() => {
                this.options.onEnd && this.options.onEnd(H);
            });
        }
    };

    _removeEvents() {
        const { $ele } = this;
        $ele.removeEventListener(touchstart, this._touchstart);
        $ele.removeEventListener(touchmove, this._touchmove);
        $ele.removeEventListener(touchend, this._touchend);
        !PHONE && $ele.removeEventListener(touchleave, this._touchend);
    }

    _addEvents() {
        const { $ele } = this;
        $ele.addEventListener(touchstart, this._touchstart, { passive: false });
        $ele.addEventListener(touchmove, this._touchmove, { passive: false });
        $ele.addEventListener(touchend, this._touchend, { passive: false });
        !PHONE && $ele.addEventListener(touchleave, this._touchend, { passive: false });
    }

    reset() {
        this.state = Object.assign(this.state || {}, {
            start: { x: 0, y: 0 },
            move: { x: 0, y: 0 },
            preventDefault: false,
            swipeDir: '',
            canStart: true,
            scrollTop: 100,
            isStart: false,
        });
    }

    disable() {
        !this.state.disable && this._removeEvents();
        this.state.disable = true;
    }

    enable() {
        this.state.disable && this._addEvents();
        this.state.disable = false;
    }

    init() {
        this.reset();
        this._addEvents();
    }
}
